#pragma once

#include <cstdint>

#include "luster_alloc.h"

namespace luster {

// 在堆上分配内存的对象。
//
// Box 不能为空。
template <typename T> class Box {
public:
	Box() : data_(allocate(alignof(T))) {
		new (data_) T;
	}

	Box(Box const &that) : data_(allocate(alignof(T))) {
		new (data_) T(that);
	}

	Box &operator=(Box const &that) {
		if (this == &that)
			return *this;
		*data_ = *that.data_;
		return *this;
	}

	Box(Box &&) = delete;
	Box &operator=(Box &&) = delete;

	T const &Get() const {
		return *data_;
	}

	T &Mut() {
		return *data_;
	}

	~Box() {
		data_->~T();
		deallocate(data_);
	}

private:
	T *data_;
};

template <typename T> class Vec {
public:
	~Vec();

	T *Append();
	void Clear();

	uintptr_t Len() const {
		return len_;
	}

	bool IsEmpty() const {
		return len_ == 0;
	}

	uintptr_t Cap() const {
		return cap_;
	}

	T const &operator[](uintptr_t i) const {
		return data_[i];
	}

	T &operator[](uintptr_t i) {
		return data_[i];
	}

private:
	void Enlarge(uintptr_t newcap);

	T *data_{nullptr};
	uintptr_t len_{0};
	uintptr_t cap_{0};
};

template <typename T> Vec<T>::~Vec() {
	Clear();
}

template <typename T> void Vec<T>::Clear() {
	for (uintptr_t i = 0; i < len_; ++i) {
		data_[i]->~T();
	}
	deallocate(data_);

	data_ = nullptr;
	len_ = 0;
	cap_ = 0;
}

template <typename T> void Vec<T>::Enlarge(uintptr_t newcap) {
	auto newdata = (T *)allocate(data_, sizeof(T) * newcap);

	for (uintptr_t i = 0; i < len_; ++i) {
		new (newdata + i) T(data_[i]);
		data_[i]->~T();
	}

	data_ = newdata;
	cap_ = newcap;
}

template <typename T> T *Vec<T>::Append() {
	if (len_ >= cap_) {
		Enlarge(cap_ == 0 ? 4 : cap_ * 3 / 2);
	}

	T *x = data_ + len_;
	len_ += 1;
	return x;
}

} // namespace luster
